﻿using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Primitives;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace ST.Common.Util.Cache
{
	public class MemoryCacheManager : ICacheManager
	{
		protected IMemoryCache mc;
		protected CancellationTokenSource cancellationTokenSource;

		public MemoryCacheManager(IMemoryCache _mc)
		{
			mc = _mc;
			cancellationTokenSource = new CancellationTokenSource();
		}

		protected MemoryCacheEntryOptions GetMemoryCacheEntryOptions(int cacheSeconds)
		{
			MemoryCacheEntryOptions opts = new MemoryCacheEntryOptions();
			opts.AddExpirationToken(new CancellationChangeToken(cancellationTokenSource.Token));
			//opts.RegisterPostEvictionCallback(PostEvictionCallback);
			opts.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(cacheSeconds);
			//opts.AbsoluteExpiration = DateTime.SpecifyKind(DateTime.Now.AddSeconds(ret.expires_in), DateTimeKind.Local);
			return opts;
		}

		protected void PostEvictionCallback(object key, object value, EvictionReason reason, object state)
		{
			////if cached item just change, then nothing doing
			//if (reason == EvictionReason.Replaced)
			//	return;

			////try to remove all keys marked as not existing
			//ClearKeys();

			////try to remove this key from dictionary
			//TryRemoveKey(key.ToString());
		}

		/// <summary>
		/// 从缓存中读取，如果读取不到调用获取函数获取然后存入缓存
		/// </summary>
		/// <typeparam name="T">Type of cached item</typeparam>
		/// <param name="key">Cache key</param>
		/// <param name="acquire">Function to load item if it's not in the cache yet</param>
		/// <param name="cacheTime">缓存时间（秒），如果传0则不缓存</param>
		/// <returns>The cached value associated with the specified key</returns>
		public T Get<T>(string key, Func<T> acquire, int cacheSeconds)
		{
			if (mc.TryGetValue<T>(key, out T value))
			{
				return value;
			}
			var result = acquire();
			if (cacheSeconds > 0)
			{
				Set(key, result, cacheSeconds);
			}
			return result;
		}

		/// <summary>
		/// 添加或更新数据到指定缓存
		/// </summary>
		/// <param name="key">缓存Key</param>
		/// <param name="data">缓存数据</param>
		/// <param name="cacheTime">缓存时间（秒）</param>
		public void Set(string key, object data, int cacheSeconds)
		{
			if (data != null)
			{
				mc.Set(key, data, GetMemoryCacheEntryOptions(cacheSeconds));
			}
		}

		/// <summary>
		/// 判断指定缓存Key是否存在
		/// </summary>
		/// <param name="key">缓存Key</param>
		/// <returns>True if item already is in cache; otherwise false</returns>
		public bool IsSet(string key)
		{
			return mc.TryGetValue(key, out object _);
		}

		/// <summary>
		/// 移除指定缓存
		/// </summary>
		/// <param name="key">缓存Key</param>
		public void Remove(string key)
		{
			mc.Remove(key);
		}

		/// <summary>
		/// Removes items by key pattern
		/// </summary>
		/// <param name="pattern">String key pattern</param>
		public void RemoveByPattern(string pattern)
		{
			throw new NotImplementedException();

		}

		/// <summary>
		/// 清除所有缓存
		/// </summary>
		public void Clear()
		{
			cancellationTokenSource.Cancel();

			//releases all resources used by this cancellation token
			cancellationTokenSource.Dispose();

			//recreate cancellation token
			cancellationTokenSource = new CancellationTokenSource();
		}

		public void Dispose()
		{
		}
	}
}
